﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using Figure_Finder;

namespace Figure_Finder
{

	class Program
	{
		private static int[ , ] _buffer;
		private static int[ , ] _tmp;
		private static List<Point> vertexes;
		private static List<Point> lines;
		private static int R = 1;

		private static List<Shape2D> shapes = new List<Shape2D>();

		static void Main( string[] args )
		{
			string inputFileName = args[ 0 ];
			ReadFile( inputFileName );

			ComputeNeighborhoodMatrix();

			for( int i = 0; i < _tmp.GetLength( 0 ); ++i )
			{
				for( int j = 0; j < _tmp.GetLength( 1 ); ++j )
				{
					if( _tmp[ i, j ] > 0 )
					{
						EdgeDetection( i, j );
					}
				}
			}

			//EdgeDetection();
			Console.WriteLine( string.Format( "Koniec.\nZnaleziono:" ) );

			foreach( Shape2D s in shapes )
				Console.Write( s.ToString() );
			Console.ReadKey( true );
		}


		private static void Print()
		{
			for( int i = 0; i < _tmp.GetLength( 0 ); ++i )
			{
				for( int j = 0; j < _tmp.GetLength( 1 ); ++j )
				{
					Console.Write( string.Format( "\t{0}", _tmp[ i, j ] ) );
				}
				Console.Write( '\n' );
			}
			Console.WriteLine();
		}


		private static void EdgeDetection( int i = 0, int j = 0 )
		{
			Point start = new Point( i, j );
			

			vertexes.Remove( start );
			Point x = new Point(start.X, start.Y);
			Point gradient = new Point();

			List<Point> shapeVertexes = null;
			do
			{
				UpdateGradient( x, ref gradient, ref shapeVertexes );
				x.X += gradient.X;
				x.Y += gradient.Y;

			} while( !( x == start ) && !( x.X < 0 || x.Y < 0 || x.X >= _tmp.GetLength( 0 ) || x.Y >= _tmp.GetLength( 1 ) ) );

			if( shapeVertexes != null && shapeVertexes.Count > 0 )
			{
				shapes.Add( new Shape2D( shapeVertexes ) );
			}
		}

		private static void UpdateGradient( Point p, ref Point gradient, ref List<Point> ver )
		{
			if( ver == null )
				ver = new List<Point>();
			int minValue = int.MaxValue;
			Point min = new Point();

			bool recalculate = false;

			int row = p.X + gradient.X;
			int col = p.Y + gradient.Y;

			if( row < 0 || row >= _tmp.GetLength( 0 ) || col < 0 || col >= _tmp.GetLength( 1 ) )
				recalculate = true;

			if( !recalculate && _tmp[ row, col ] == 0 )
				recalculate = true;

			if( !recalculate && p == new Point( row, col ) )
				recalculate = true;
			
			if( recalculate )
			{
				for( int r = 1; r <= R; ++r )
				{
					for( int angle = 0; angle < 360; ++angle )
					{
						Point x = new Point();
						x.X = ( int ) Math.Round( ( p.X + r * Math.Cos( angle * ( Math.PI / 180 ) ) ) );
						x.Y = ( int ) Math.Round( ( p.Y + r * Math.Sin( angle * ( Math.PI / 180 ) ) ) );

						if( x.X < 0 || x.Y < 0 || x.X >= _tmp.GetLength( 0 ) || x.Y >= _tmp.GetLength( 1 ) )
							continue;

						if( _tmp[ x.X, x.Y ] > 0 && _tmp[ x.X, x.Y ] <= minValue )
						{

							minValue = _tmp[ x.X, x.Y ];
							min = x;
						}
					}
				}
				if( min != p && minValue < int.MaxValue )
				{
					
					//else
					//    --_tmp[ p.X, p.Y ];

					gradient = new Point( min.X - p.X, min.Y - p.Y );
					//Console.WriteLine( string.Format( "Wierzcholek: {0} {1}", p.X, p.Y ) );
					ver.Add( p );
					vertexes.Remove( p );
				}
			}
			if( _tmp[ p.X, p.Y ] > 0 )
				_tmp[ p.X, p.Y ] = -1;

			vertexes.Remove( p );
			Print();
		}

		private static void ComputeNeighborhoodMatrix()
		{
			_tmp = new int[ _buffer.GetLength( 0 ), _buffer.GetLength( 1 ) ];
			Dictionary<int, List<Point>> hash = new Dictionary<int, List<Point>>();
			for( int i = 0; i < _buffer.GetLength( 0 ); ++i )
			{
				for( int j = 0; j < _buffer.GetLength( 1 ); ++j )
				{
					int sum = Neighborhood( i, j );
					if( sum > 0 )
					{
						if( !hash.ContainsKey( sum ) )
							hash.Add( sum, new List<Point>() );
						hash[ sum ].Add( new Point( i, j ) );						
					}
					_tmp[ i, j ] = sum;
				}
			}

			int maxIndex = hash.Keys.Max<int>();
			int minIndex = hash.Keys.Min<int>();


			
			if( hash.Count > 2 )
			{
				foreach( Point p in hash[ maxIndex ].ToList() )
				{
					_tmp[ p.X, p.Y ] = 0;
				}
				hash.Remove( maxIndex );
			}

			if( vertexes == null ) vertexes = new List<Point>();
			vertexes.Clear();
			foreach( int k in hash.Keys.ToList() )
			{
				vertexes.AddRange( hash[ k ] );	
			}
		}

		private static int Neighborhood( int x, int y )
		{
			if( _buffer[ x, y ] == 0 )
				return 0;

			int startX, startY, endX, endY;
			startX = x - 1;
			startY = y - 1;
			endX = x + 1;
			endY = y + 1;

			if( startX < 0 )
				startX = 0;
			if( startY < 0 )
				startY = 0;

			if( endX >= _buffer.GetLength( 0 ) )
				endX = _buffer.GetLength( 0 ) - 1;

			if( endY >= _buffer.GetLength( 1 ) )
				endY = _buffer.GetLength( 1 ) - 1;

			int sum = 0;

			for( int i = startX; i <= endX; ++i )
			{
				for( int j = startY; j <= endY; ++j )
				{
					if( i == x && j == y )
						continue;
					if( _buffer[ i, j ] == 1 )
						++sum;
				}
			}
			return sum;
		}

		private static void ReadFile( string name )
		{
			string buffer = string.Empty;
			using( StreamReader sr = new StreamReader( name ) )
			{
				buffer = sr.ReadToEnd();
			}

			string[] data = buffer.Split( Environment.NewLine.ToArray() );
			int rows = data.Count();
			if( data[ data.Count() - 1 ].Length == 0 )
			{
				--rows;
			}
			int cols = data[ 0 ].Length;

			_buffer = new int[ rows, cols ];
			int i = 0;
			int j = 0;
			
			foreach( string s in data )
			{
				if( i >= rows )
					break;
				foreach( char c in s )
				{
					_buffer[ i, j++ ] = int.Parse( c.ToString() );
				}
				++i;
				j = 0;
			}
		}
	}
}
